#define _J_DRETVE_C_
#include "dretve.h"

#include "pozivi_jezgre.h"
#include "razno.h"
#include "liste.h"
#include "postavke.h"
#include "ispis.h"
#include <arh/kontekst.h>

/******************************************************************************/
/*	>>>>>>>>	Podaci jezgre (vecina)		>>>>>>>>	      */
/******************************************************************************/

//stogovi za dretve
uint32	j_stog_dretve[NAJ_DRETVI][VELICINA_STOGA];

//stog za jezgrine funkcije (kada se zovu iz prekida)
uint32	j_stogjf[VELICINA_STOGA];
uint32	*j_prekidni_stog = POCETAK_STOGA(j_stogjf, VELICINA_STOGA); //vrh stoga


//OPISNICI dretvi
opisnikdretve_t j_dretva[NAJ_DRETVI];
MASKA(j_dretva, NAJ_DRETVI);

//pokazivac na opisnik AKTIVNE dretve
opisnikdretve_t *j_aktivna;
uint32 **j_stog_aktivne;	//promjenom aktivne dretve treba azurirat i ovo

//lista PRIPRAVNIH dretvi (struktura)
lista_dretvi_t	j_pripravne_dretve[BRRAZPRI];
uint32 mj_pripravne_dretve[VELMASKE(BRRAZPRI)] = {0};

/******************************************************************************/
/*	<<<<<<<<	Podaci jezgre (vecina)		<<<<<<<<	      */
/******************************************************************************/

//interne funkcije samo za jezgra/dretve.c
static void j_pokreni_dretvu (void (*pocetna_funkcija)(void *), void *param);
static void j_stavi_u_pripravne (opisnikdretve_t *opd);
static void j_latentna_dretva (void *x);

void j_visedretvenost_inicijaliziraj()
{
	int i;

	//pocetna dretva
	j_dretva[0].id = 0;
	j_dretva[0].prio = 0;
	j_dretva[0].rasp = RASP_PRP;
	j_dretva[0].iduci = NULL;
	j_dretva[0].red = AKTIVNA;

#ifdef	KORISTI_MASKE
	postavi_bit_32(mj_dretva, NAJ_DRETVI, 0, FALSE);
#endif
	j_aktivna = &j_dretva[0];
	j_stog_aktivne = &j_aktivna->stog;

	for (i = 0; i < BRRAZPRI; i++) {
		j_pripravne_dretve[i].prvi = NULL;
		j_pripravne_dretve[i].zadnji = NULL;
	}

	for (i = 1; i < NAJ_DRETVI; i++) {
		j_dretva[i].slobodno = SLOBODNO;
		j_dretva[i].red = -1;
	}

	//stvori latentnu dretvu (idle dretvu)
	parametri_dretve_t pd;

	pd.poc_funkc = j_latentna_dretva;
	pd.param = NULL;
	pd.stog = NULL;
	pd.prio = PRIO_MIN;
	pd.rasp = RASP_PRP;

	j_stvori_dretvu (&pd);
}

//pomocne funkcije
void j_samo_stavi_u_pripravne (opisnikdretve_t *opd)
{
	DODAJ_NA_KRAJ(&j_pripravne_dretve[opd->prio], opd);
	opd->red = PRIPRAVNA;
	postavi_bit_32(mj_pripravne_dretve, BRRAZPRI, opd->prio, TRUE);
}

static void j_stavi_u_pripravne (opisnikdretve_t *opd)
{
	j_samo_stavi_u_pripravne(opd);

	j_aktiviraj_prvu_pripravnu();
}

void j_aktiviraj_prvu_pripravnu ()
{
	int prio = pronadji_bit_32(mj_pripravne_dretve, BRRAZPRI);
	
	//aktivna se mijenja?
	if (j_aktivna == NULL || j_aktivna->red != AKTIVNA || 
			(prio != -1 && j_aktivna->prio > prio))
	{
		if (prio == -1 || j_pripravne_dretve[prio].prvi == NULL) {
			//j_ispisi("prio=%d\n", prio);
			j_zaustavi_potpuno("Red pripravnih prazan!\n");
		}
		
		//stavi trenutno aktivnu u pripravne (jer ce sad druga bit akt.)
		if (j_aktivna != NULL && j_aktivna->red == AKTIVNA)
			j_samo_stavi_u_pripravne(j_aktivna);

		j_aktivna = j_pripravne_dretve[prio].prvi;

		j_pripravne_dretve[prio].prvi = j_aktivna->iduci;

		if (j_pripravne_dretve[prio].prvi == NULL)
			postavi_bit_32(mj_pripravne_dretve,BRRAZPRI,prio,FALSE);

		j_aktivna->iduci = NULL;
		j_stog_aktivne = &j_aktivna->stog;
		j_aktivna->red = AKTIVNA;
	}
}

//pocetna funkcija za sve dretve, iz argumenata se uzima funkcija koju je zadao
//korisnik te se ona poziva;
//kada dodje do kraja, dretva zavrsava - NIJE jezgrina funkcija
static void j_pokreni_dretvu (void (*pocetna_funkcija)(void *), void *param)
{
	pocetna_funkcija(param);
	
	//oznaci zavrsetak dretve
	j_funkcija(DRETVA_ZAVRSETAK, NULL);
}


//stvaranje nove dretve - poziva se iz prekida
int j_stvori_dretvu (void *param)
{
	parametri_dretve_t *pd = param;
	int i, povr_vr = 0;
	opisnikdretve_t *opd;

	PARAMNULL(pd);

	if (pd->poc_funkc == NULL) {
		j_ispisi("poc_funkc je NULL!");
		povr_vr = -1;
	}
	else {
		i = DOHVATI(j_dretva);

		if (i != -1) {
			opd = &j_dretva[i];
			opd->id = i;
			opd->stog = pd->stog;
			opd->iduci = NULL;
			opd->prio = pd->prio;
			opd->rasp = pd->rasp;
			opd->red = -1;

			//postavi kazaljku stoga, a stog sredi tako da se
			//povratkom iz prekida vraca/pocinje s ovom dretvom

			if (opd->stog == NULL) //postavi stog
				opd->stog = POCETAK_STOGA(j_stog_dretve[i],
							VELICINA_STOGA);
			
			//stavi parametre na stog za funkciju: j_pokreni_dretvu
			STAVI_NA_STOG(opd->stog, pd->param); //2. parametar fun.
			STAVI_NA_STOG(opd->stog, pd->poc_funkc); //1.parametar

			STAVI_NA_STOG(opd->stog, 0);
			//U normalnom pozivu potprograma nakon parametara,
			//instrukcija call na stog stavlja povratnu adresu.
			//Za dretvu je ovo pocetna funkcija iz koje se ne vraca
			//pa vrijednost 0 je odgovarajuca
			
			opd->stog = arh_izgradi_prekidni_stog(opd->stog,
							j_pokreni_dretvu);
			
			//dodaj dretvu u pripravne
			j_stavi_u_pripravne(opd);

			//povratna vrijednost jezgrine funkcije - id dretve
			povr_vr = i;
		}
		else {
			j_ispisi("Nema slobodna opisnika za dretvu");
			povr_vr = -1;
		}
	}
	
	return povr_vr;
}


//'jezgrina funkcija', poziva se iz prekida
//daj procesor iducoj dretvi iz reda pripravnih
int j_dretva_odstupi (void *param)
{
	opisnikdretve_t *akt = j_aktivna;

	j_stavi_u_pripravne(j_aktivna);

	if (akt != j_aktivna)
		return 0; //dogodila se promjena dretve
	else
		return 1; //nije se dogodila promjena dretve
}

//kraj rada aktivne dretve, makni je iz reda aktivne
int j_dretva_zavrsetak (void *param)
{
	opisnikdretve_t *akt = j_aktivna;
	
	//j_obrisi_dretvu(j_aktivna);
	akt->red = PASIVNA;

	OBRISI(j_dretva, akt->id);

	j_aktiviraj_prvu_pripravnu();

	return 0;
}

//vraca id trenutne dretve (moze se pozvati i bez prekida za sada)
int j_id_dretve (void *param)
{
	return j_aktivna->id;
}

//prioriteti
int j_dohvati_prioritet (void *param)
{
	int *pid = param, id, prio = -1;
	
	if (pid == NULL || (id = *pid) == 0)
		id = j_aktivna->id;
		
	if (id >= 0 && id < NAJ_DRETVI && j_dretva[id].slobodno != SLOBODNO)
		prio = j_dretva[id].prio;

	return prio;
}

//promijeni prioritet aktivnoj dretvi (ona poziva ovu funkciju)
int j_postavi_prioritet (void *param)
{
	int *pprio = param, prio, sprio = j_aktivna->prio;
	
	PARAMNULL(pprio);

	if (j_aktivna->id == 0) {
		j_ispisi("Pocetna dretva ne moze mijenjati prioritet!\n");
		return -1;
	}
	
	prio = *pprio;

	if (prio < PRIO_MAX || prio > PRIO_MIN) {
		j_ispisi("Prioritet izvan dozvoljenih granica!\n");
		return -1;
	}
		
	j_aktivna->prio = prio;
	
	if (prio > sprio) //smanjio prioritet, mozda je sad neka druga prije
		j_aktiviraj_prvu_pripravnu();

	return sprio;
}

//aktivira se kad nema nista drugo
static void j_latentna_dretva (void *x)
{
	while(1)
		j_zaustavi();
}
